package com.mars.push.core.channel.side;

import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import com.mars.fw.common.utils.StringUtils;
import com.mars.push.api.common.*;
import com.mars.push.api.dto.PushMessageInput;
import com.mars.push.api.exception.PushException;
import com.mars.push.core.channel.AbstractPushChannel;
import com.mars.push.core.channel.PushMessage;
import com.mars.push.core.channel.side.config.JpushConfig;
import com.mars.push.core.channel.side.dto.JPushMessage;
import com.mars.push.core.common.JsonMapper;
import com.mars.push.core.entity.PushMessageEntity;
import com.mars.push.core.repository.dao.PushMessageDao;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Author King
 * @create 2020/7/10 11:44
 */
@Slf4j
@Component
public class JPushChannel extends AbstractPushChannel {

    @Autowired
    private JpushConfig config;

    @Autowired
    private PushMessageDao pushMessageDao;


    @Override
    protected Object iosPush0(PushMessage pushMessage) {
        JPushMessage message = (JPushMessage) pushMessage;
        JPushClient jpushClient = new JPushClient(config.getMasterSecret(), config.getAppKey(), null, ClientConfig.getInstance());
        PushResult result = null;
        try {
            PushPayload payload = buildIosPushObject(message);
            result = jpushClient.sendPush(payload);
            log.info("推送结果:{}", result);
            if (result.statusCode == 0) {
                onSuccess(pushMessage, result);
            } else {
                onFail(pushMessage, result);
            }
        } catch (APIConnectionException ex) {
            log.error("连接错误,稍后再试", ex);
            onException(pushMessage, ex.getMessage());
            throw new PushException("推送服务连接错误", ex);
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.info("HTTP Status: " + e.getStatus());
            log.info("Error Code: " + e.getErrorCode());
            log.info("Error Message: " + e.getErrorMessage());
            onException(pushMessage, e.getMessage());
            throw new PushException("推送异常" + "HTTP Status: " + e.getStatus() + "Error Code: " + e.getErrorCode() + "Error Message: " + e.getErrorMessage(), e);
        }
        return result;
    }

    @Override
    protected Object androidPush0(PushMessage pushMessage) {
        JPushMessage message = (JPushMessage) pushMessage;
        JPushClient jpushClient = new JPushClient(config.getMasterSecret(), config.getAppKey(), null, ClientConfig.getInstance());
        PushResult result = null;
        try {
            PushPayload payload = buildAndroidPushObject(message);
            result = jpushClient.sendPush(payload);
            log.info("推送结果:{}", result);
            if (result.statusCode == 0) {
                onSuccess(pushMessage, result);
            } else {
                onFail(pushMessage, result);
            }
        } catch (APIConnectionException ex) {
            log.error("连接错误,稍后再试", ex);
            onException(pushMessage, ex.getMessage());
            throw new PushException("推送服务连接错误", ex);
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.info("HTTP Status: " + e.getStatus());
            log.info("Error Code: " + e.getErrorCode());
            log.info("Error Message: " + e.getErrorMessage());
            onException(pushMessage, e.getMessage());
            throw new PushException("推送异常" + "HTTP Status: " + e.getStatus() + "Error Code: " + e.getErrorCode() + "Error Message: " + e.getErrorMessage(), e);
        }
        return result;
    }


    @Override
    protected Object allPush0(PushMessage pushMessage) {
        JPushMessage message = (JPushMessage) pushMessage;
        JPushClient jpushClient = new JPushClient(config.getMasterSecret(), config.getAppKey(), null, ClientConfig.getInstance());
        PushResult result = null;
        try {
            PushPayload payload = buildAllPushObject(message);
            result = jpushClient.sendPush(payload);
            log.info("推送结果:{}", result);
            if (result.statusCode == 0) {
                onSuccess(pushMessage, result);
            } else {
                onFail(pushMessage, result);
            }
        } catch (APIConnectionException ex) {
            log.error("连接错误,稍后再试", ex);
            onException(pushMessage, ex.getMessage());
            throw new PushException("推送服务连接错误", ex);
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.info("HTTP Status: " + e.getStatus());
            log.info("Error Code: " + e.getErrorCode());
            log.info("Error Message: " + e.getErrorMessage());
            onException(pushMessage, e.getMessage());
            throw new PushException("推送异常" + "HTTP Status: " + e.getStatus() + "Error Code: " + e.getErrorCode() + "Error Message: " + e.getErrorMessage(), e);
        }
        return result;
    }

    @Override
    public PushMessage createMessage(MessageInput messageInput) {
        JPushMessage message = new JPushMessage();
        PushMessageInput source = (PushMessageInput) messageInput;
        BeanUtils.copyProperties(source, message);
        return message;
    }


    @Override
    protected void onSuccess(PushMessage message, Object result) {
        PushResult re = (PushResult) result;
        JPushMessage jPushMessage = (JPushMessage) message;
        PushMessageEntity entity = new PushMessageEntity();
        entity.setChannelResponse(re.toString())
                .setContent(JsonMapper.getDefault().toJson(jPushMessage))
                .setChannelType(PushType.J_PUSH.getCode())
                .setMessageType(jPushMessage.getPushMessageType().getCode())
                .setBusinessFlag(jPushMessage.getBusinessType())
                .setStatus(PushStatusEnum.SUCCESS.getCode());
        pushMessageDao.insert(entity);
    }

    @Override
    protected void onFail(PushMessage message, Object result) {
        PushResult re = (PushResult) result;
        JPushMessage jPushMessage = (JPushMessage) message;
        PushMessageEntity entity = new PushMessageEntity();
        entity.setChannelResponse(re.toString())
                .setContent(JsonMapper.getDefault().toJson(jPushMessage))
                .setChannelType(PushType.J_PUSH.getCode())
                .setMessageType(jPushMessage.getPushMessageType().getCode())
                .setStatus(PushStatusEnum.FAILURE.getCode());
        pushMessageDao.insert(entity);
    }

    @Override
    protected void onException(PushMessage message, Object result) {
        JPushMessage jPushMessage = (JPushMessage) message;
        PushMessageEntity entity = new PushMessageEntity();
        entity.setChannelResponse(result.toString())
                .setContent(JsonMapper.getDefault().toJson(jPushMessage))
                .setChannelType(PushType.J_PUSH.getCode())
                .setMessageType(jPushMessage.getPushMessageType().getCode())
                .setStatus(PushStatusEnum.FAILURE.getCode());
        pushMessageDao.insert(entity);
    }


    /**
     * 构建Android推送的参数
     *
     * @param jPushMessage
     * @return
     */
    private PushPayload buildAndroidPushObject(JPushMessage jPushMessage) {
        PushPayload.Builder builder = PushPayload.newBuilder()
                .setPlatform(Platform.android())
                .setAudience(createAudience(jPushMessage.getTargets(), jPushMessage.getNoticeType()));
        if (PushMessageType.NOTICE.equals(jPushMessage.getPushMessageType())) {
            builder.setNotification(Notification.android(jPushMessage.getAlert(), jPushMessage.getTitle(), jPushMessage.getExtras()));
        } else if (PushMessageType.MESSAGE.equals(jPushMessage.getPushMessageType())) {
            builder.setMessage(createMessage(jPushMessage));
        } else {
            builder.setNotification(Notification.android(jPushMessage.getAlert(), jPushMessage.getTitle(), jPushMessage.getExtras()))
                    .setMessage(createMessage(jPushMessage));
        }
        return builder.build();
    }


    /**
     * 构建Ios推送的参数
     *
     * @param jPushMessage
     * @return
     */
    private PushPayload buildIosPushObject(JPushMessage jPushMessage) {
        PushPayload.Builder builder = PushPayload.newBuilder()
                .setPlatform(Platform.ios())
                .setOptions(Options.newBuilder().setApnsProduction(config.isApnsProduction()).build())
                .setAudience(createAudience(jPushMessage.getTargets(), jPushMessage.getNoticeType()));
        if (PushMessageType.NOTICE.equals(jPushMessage.getPushMessageType())) {
            builder.setNotification(Notification.ios(jPushMessage.getAlert(), jPushMessage.getExtras()));
        } else if (PushMessageType.MESSAGE.equals(jPushMessage.getPushMessageType())) {
            builder.setMessage(createMessage(jPushMessage));
        } else {
            builder.setNotification(Notification.ios(jPushMessage.getAlert(), jPushMessage.getExtras()))
                    .setMessage(createMessage(jPushMessage));
        }
        return builder.build();
    }


    /**
     * 构建Ios推送的参数
     *
     * @param jPushMessage
     * @return
     */
    private PushPayload buildAllPushObject(JPushMessage jPushMessage) {

        PushPayload.Builder builder = PushPayload.newBuilder()
                // 指定要推送的平台，all代表当前应用配置了的所有平台，也可以传android等具体平台
                .setPlatform(Platform.android_ios())
                .setOptions(Options.newBuilder().setApnsProduction(config.isApnsProduction()).build())
                // 指定推送的接收对象，all代表所有人，也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registrationId
                .setAudience(createAudience(jPushMessage.getTargets(), jPushMessage.getNoticeType()));
        if (PushMessageType.NOTICE.equals(jPushMessage.getPushMessageType())) {
            builder.setNotification(Notification.newBuilder()
                    .addPlatformNotification(AndroidNotification.newBuilder()
                            .setAlert(jPushMessage.getAlert())
                            .setTitle(jPushMessage.getTitle())
                            .addExtras(jPushMessage.getExtras())
                            .build())
                    .addPlatformNotification(createIosNotification(jPushMessage))
                    .build());
        } else if (PushMessageType.MESSAGE.equals(jPushMessage.getPushMessageType())) {
            builder.setMessage(createMessage(jPushMessage));
        } else {
            // jpush的通知，android的由jpush直接下发，iOS的由apns服务器下发，Winphone的由mpns下发
            builder.setNotification(Notification.newBuilder()
                    .addPlatformNotification(AndroidNotification.newBuilder()
                            .setAlert(jPushMessage.getAlert())
                            .setTitle(jPushMessage.getTitle())
                            //此字段为透传字段，不会显示在通知栏。用户可以通过此字段来做一些定制需求，如特定的key传要指定跳转的页面（value）
                            .addExtras(jPushMessage.getExtras())
                            .build())
                    .addPlatformNotification(createIosNotification(jPushMessage))
                    .build())
                    //jpush的自定义消息
                    .setMessage(createMessage(jPushMessage));
        }
        return builder.build();
    }


    /**
     * 构建自定义消息实体
     *
     * @param jPushMessage
     * @return
     */
    private Message createMessage(JPushMessage jPushMessage) {
        Message.Builder builder = Message.newBuilder();
        builder.setMsgContent(jPushMessage.getCustomerMessage());
        if (StringUtils.isNotBlank(jPushMessage.getContentType())) {
            builder.setContentType(jPushMessage.getContentType());
        }
        if (null != jPushMessage.getCustomerMessageExtras()) {
            builder.addExtras(jPushMessage.getCustomerMessageExtras());
        }
        builder.setTitle(jPushMessage.getTitle());
        return builder.build();
    }


    /**
     * 构建IosNotification的实体
     *
     * @param jPushMessage
     * @return
     */
    private IosNotification createIosNotification(JPushMessage jPushMessage) {

        IosNotification.Builder builder = IosNotification.newBuilder()
                // 传一个IosAlert对象，指定apns title、title、subtitle等
                .setAlert(jPushMessage.getAlert())
                // 此字段为透传字段，不会显示在通知栏。用户可以通过此字段来做一些定制需求，如特定的key传要指定跳转的页面（value）
                .addExtras(jPushMessage.getExtras());
        if (null != jPushMessage.getBadge()) {
            // 此项是指定此推送的badge自动加1
            builder.setBadge(jPushMessage.getBadge());
        }
        if (StringUtils.isNotBlank(jPushMessage.getThreadId())) {
            builder.setThreadId(jPushMessage.getThreadId());
        }
        if (null != jPushMessage.getSound()) {
            // 此字段的值default表示系统默认声音；传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒
            builder.setSound(jPushMessage.getSound());
        }
        if (jPushMessage.isContentAvailable()) {
            builder.setContentAvailable(jPushMessage.isContentAvailable());
        }

        return builder.build();
    }


    /**
     * 构建推送目标
     *
     * @param targets
     * @param noticeType
     * @return
     */
    private Audience createAudience(List<String> targets, NoticeType noticeType) {
        if (noticeType.equals(NoticeType.ALIAS)) {
            return Audience.alias(targets);
        } else if (noticeType.equals(NoticeType.REGISTER_ID)) {
            return Audience.registrationId(targets);
        } else if (noticeType.equals(NoticeType.TAG)) {
            return Audience.tag(targets);
        } else {
            return Audience.all();
        }
    }
}
